package similarite;

import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;

import Jama.EigenvalueDecomposition;
import Jama.Matrix;

public class MDScaling {

	private Matrix JMatrix;
	private Matrix BMatrix;
	private Matrix DMatrix;
	private int indexOfFirstEigenVector = 0;
	private int indexOfSecondEigenVector = 0;
	
	public MDScaling(Matrix P) 
	{
		createDMatrixWithPMatrix(P);
		initJMatrix();
		initBMatrix();

		Matrix X = getXMatrix();
//		X.print(10, 2);
		PrintWriter pw = null;
		try {
	    	pw = new PrintWriter(new FileWriter("src/PMatrix.txt"));
			P.print(pw, P.getRowDimension(), P.getColumnDimension());
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	    pw.flush();
	    pw.close();
	    try {
	    	pw = new PrintWriter(new FileWriter("src/coord.txt"));
			X.print(pw, X.getRowDimension(), X.getColumnDimension());
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	    pw.flush();
	    pw.close();
	}
	
	public void createDMatrixWithPMatrix(Matrix P)
	{
//		System.out.println("P MATRIX : ");
//		P.print(10, 2);
		DMatrix = new Matrix(P.getRowDimension(), P.getColumnDimension());
		for (int i = 0; i < P.getColumnDimension(); i++) {
			for (int j = 0; j < P.getRowDimension(); j++) {
				DMatrix.set(i, j, Math.log(P.get(i, j)));
//				DMatrix.set(i, j, Math.pow(DMatrix.get(i, j), 2));
			}
		}
		
		createDPrimeMatrixWithDMatrix();
	}
	
	public void createDPrimeMatrixWithDMatrix()
	{
		for (int i = 0; i < DMatrix.getColumnDimension(); i++) {
			for (int j = 0; j < DMatrix.getRowDimension(); j++) {
				DMatrix.set(i, j, Math.pow(DMatrix.get(i, j), 2));
			}
		}
	}
	
	private void initJMatrix() 
	{
		int rowDim = DMatrix.getRowDimension();
		int columnDim = DMatrix.getColumnDimension();
		double[][] J = new double[rowDim][columnDim];
		for (int i = 0; i< rowDim; i++) {
			for (int j = 0; j < columnDim; j++) {
				if (i == j) {
					J[i][j] = 1-1/rowDim;
				}
				else {
					J[i][j] = -1/columnDim;
				}
			}
		}
		
		JMatrix = new Matrix(J);
	}
	
	private void initBMatrix() 
	{
		BMatrix = new Matrix(DMatrix.getRowDimension(), DMatrix.getColumnDimension());
		Matrix tmp = JMatrix.times(DMatrix);
//		System.out.println("B MATRIX CREATION");
//		JMatrix.print(10, 2);
//		DMatrix.print(10, 2);
		BMatrix = tmp.times(JMatrix);
		BMatrix = BMatrix.times(-0.5);
	}
	
	private double[] getEigenValuesOfBMatrix()
	{
		EigenvalueDecomposition eigenValueDecomposition = BMatrix.eig();
		
		return eigenValueDecomposition.getRealEigenvalues();
	}
	
	private Matrix getLMatrix()
	{
		Matrix LMatrix = new Matrix(2, 2);
		double[] eigenValues = getEigenValuesOfBMatrix();
		
		double max1 = (eigenValues[0]>eigenValues[1]?eigenValues[0]:eigenValues[1]);
		double max2 = (eigenValues[0]>eigenValues[1]?eigenValues[1]:eigenValues[0]);

		indexOfFirstEigenVector = (max1 == eigenValues[0])?0:1;
		indexOfSecondEigenVector = (max2 == eigenValues[0])?0:1;

		for (int i = 2; i < eigenValues.length; i++) {
			if (eigenValues[i] > max1) {
				max2 = max1;
				max1 = eigenValues[i];
				indexOfSecondEigenVector = indexOfFirstEigenVector;
				indexOfFirstEigenVector = i;
			}
			else if (eigenValues[i] > max2) {
				max2 = eigenValues[i];
				indexOfFirstEigenVector = i;
			}
		}
		
		LMatrix.set(0, 0, Math.sqrt(max1));
		LMatrix.set(1, 1, Math.sqrt(max2));
		
		return LMatrix;
	}
	
	private Matrix getEMatrix()
	{
		Matrix eigenVectors = BMatrix.eig().getV();
		Matrix EMatrix = new Matrix(eigenVectors.getRowDimension(), 2);
		int[] rowIndexes = new int[1];
		rowIndexes[0] = indexOfFirstEigenVector;
		Matrix firstVector = eigenVectors.getMatrix(rowIndexes, 0, eigenVectors.getRowDimension()-1);
		
		rowIndexes[0] = indexOfSecondEigenVector;
		Matrix SecondVector = eigenVectors.getMatrix(rowIndexes, 0, eigenVectors.getRowDimension()-1);
		firstVector = firstVector.transpose();
		SecondVector = SecondVector.transpose();
		EMatrix.setMatrix(0, firstVector.getRowDimension()-1, 0, 0, firstVector);
		EMatrix.setMatrix(0, firstVector.getRowDimension()-1, 1, 1, SecondVector);
		
		return EMatrix;
	}
	
	public Matrix getXMatrix()
	{
//		BMatrix.print(10, 2);
//		System.out.println("L MATRIX : ");
		Matrix L = getLMatrix();
//		L.print(10, 2);
		
//		System.out.println("E MATRIX : ");
		Matrix E = getEMatrix();
//		E.print(10, 2);

//		System.out.println("X MATRIX : ");
		return E.times(L);
	}
}
